/*
 ============================================================================
 Name        : WebServer.c
 Author      : 
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "rio.h"

#include <sys/mman.h>
#include "MyQueue.h"

void doit(int fd);
//void* doit(void* fdPtr);
void read_requesthdrs(rio_t *rp);
int parse_uri(char *uri, char *filename, char *cgiargs);
void serve_static(int fd, char *filename, int filesize);
void get_filetype(char *filename, char *filetype);
void clienterror(int fd, char *cause, char *errnum, char *shortmsg,
		char *longmsg);
void* gurad_thread(void* no_use_ptr);

sem_t q_mutex;
sem_t thr_balance_mutex;
sem_t thr_sem;

int thread_count;
int threab_balance;
//extern struct MyQueue;
struct MyQueue *fdQueue;

void CalcThreadCount() {
	int estmate_tc = fdQueue->length / 3;
	if (estmate_tc > 30)
		estmate_tc = 30;
	if (estmate_tc > 30) {
		if (estmate_tc != thread_count) {
			P(&thr_balance_mutex);

			threab_balance = estmate_tc - thread_count;
			V(&thr_balance_mutex);
		}
		if (threab_balance > 0) {
			P(&thr_balance_mutex);
			pthread_t tid1;
			pthread_create(&tid1, NULL, gurad_thread, &thread_count);
			thread_count++;
			threab_balance--;
			V(&thr_balance_mutex);
		}
	}
}

void* doit_thread(void* fdPtr) {
	int fd = *((int *) fdPtr);
	doit(fd);
	close(fd);
	return NULL;
}

int main(void) {
	int listenfd, connfd, port, clientlen;
	struct sockaddr_in clientaddr;

	port = 8089;

	fdQueue = malloc(sizeof(struct MyQueue));
	sem_init(&q_mutex, 0, 1);
	sem_init(&thr_sem, 0, 0);
	sem_init(&thr_balance_mutex, 0, 1);
	initQueue(fdQueue);

	thread_count = 0;
	threab_balance = 0;
	while (thread_count < 30) {
		pthread_t tid1;
		int thr_Number = thread_count;
		pthread_create(&tid1, NULL, gurad_thread, &thr_Number);
		thread_count++;
	}

	listenfd = Open_listenfd(port);
	while (1) {
		//pthread_t tid1;
		clientlen = sizeof(clientaddr);
		connfd = accept(listenfd, (SA*) &clientaddr, (socklen_t*) &clientlen);
		//pthread_create(&tid1, NULL, doit_thread, &connfd);
		P(&q_mutex);
		enQueue(fdQueue, connfd);
		V(&q_mutex);
		V(&thr_sem);
		//CalcThreadCount();
		//pthread_create(&tid1, NULL, doit, &connfd);

		/*		if (fork() == 0) {
		 close(listenfd);
		 doit(connfd);
		 close(connfd);
		 exit(0);
		 }
		 close(connfd);
		 */
	}
	return EXIT_SUCCESS;
}
int req_count = 0;
int thread_Number = 0;
void* gurad_thread(void* no_use_ptr) {
	int t_num = ++thread_Number;
	while (1) {
		P(&thr_sem);
		if (fdQueue->length > 0) {
			P(&q_mutex);
			int fd = outQueue(fdQueue);
			V(&q_mutex);
			printf("request number: %d \n", req_count++);
			printf("thread number: %d \n", t_num);
//			printf("fd number: %d \n", fd);
//			printf("thread number: %d \n", thread_count);
//			printf("thread balance number: %d \n", threab_balance);
			doit(fd);
//			printf("close by gurad_thread: %d \n", fd);
			close(fd);
//			if (threab_balance < 0) {
//				P(&thr_balance_mutex);
//				thread_count--;
//				threab_balance++;
//				V(&thr_balance_mutex);
//				pthread_exit(NULL);
//			}
		}
	}
	return NULL;
}

//void* doit(void* fdPtr) {
//	int fd = *((int *) fdPtr);
void doit(int fd) {
	int is_static;
	struct stat sbuf;
	char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
	char filename[MAXLINE], cgiargs[MAXLINE];
	rio_t rio;

	Rio_readinitb(&rio, fd);
	Rio_readlineb(&rio, buf, MAXLINE);
	sscanf(buf, "%s %s %s", method, uri, version);
	if (strcasecmp(method, "GET")) {
		clienterror(fd, method, "501", "Not Implemented",
				"Tiny does not implement this method");
		return;
	}
	printf("%s \n", uri);
	read_requesthdrs(&rio);
	is_static = parse_uri(uri, filename, cgiargs);

	if (stat(filename, &sbuf) < 0) {
		clienterror(fd, method, "404", "Not found",
				"Tiny couldn't found this file.");
		;
		return;
	}

	if (is_static) {
		if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) {
			clienterror(fd, method, "403", "Forbidden",
					"Tiny couldn't read this file.");
			return;
		}
		serve_static(fd, filename, sbuf.st_size);
	} else {
		if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) {
			clienterror(fd, filename, "403", "Forbidden",
					"Tiny couldn’t run the CGI program");
			return;
		}
		//serve_dynamic(fd, filename, cgiargs);
	}
	//close(fd);
	//printf("close by doit, %d \n",fd);
	return;
}

void serve_static(int fd, char *filename, int filesize) {
	int srcfd;
	char *srcp, filetype[MAXLINE], buf[MAXBUF];

	/* Send response headers to client */
	get_filetype(filename, filetype);
	sprintf(buf, "HTTP/1.0 200 OK\r\n");
	sprintf(buf, "%sServer: Tiny Web Server\r\n", buf);
	sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
	sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);
	Rio_writen(fd, buf, strlen(buf));
	/* Send response body to client */
	srcfd = open(filename, O_RDONLY, 0);
	srcp = mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
	close(srcfd);
	Rio_writen(fd, srcp, filesize);
	munmap(srcp, filesize);
}

void clienterror(int fd, char *cause, char *errnum, char *shortmsg,
		char *longmsg) {
	char buf[MAXLINE], body[MAXBUF];
	/* Build the HTTP response body */
	sprintf(body, "<html><title>Tiny Error</title>");
	sprintf(body, "%s<body bgcolor=" "ffffff" ">\r\n", body);
	sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);
	sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause);
	sprintf(body, "%s<hr><em>The Tiny Web server</em>\r\n", body);
	/* Print the HTTP response */
	sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
	Rio_writen(fd, buf, strlen(buf));
	sprintf(buf, "Content-type: text/html\r\n");
	Rio_writen(fd, buf, strlen(buf));
	sprintf(buf, "Content-length: %d\r\n\r\n", (int) strlen(body));
	Rio_writen(fd, buf, strlen(buf));
	Rio_writen(fd, body, strlen(body));
}

void read_requesthdrs(rio_t *rp) {
	char buf[MAXLINE];
	Rio_readlineb(rp, buf, MAXLINE);
	while (strcmp(buf, "\r\n")) {
		Rio_readlineb(rp, buf, MAXLINE);
		//printf("%s", buf);
	}
	return;
}

int parse_uri(char *uri, char *filename, char *cgiargs) {
	char *ptr;
	if (!strstr(uri, "cgi-bin")) { /* Static content */
		strcpy(cgiargs, "");
		strcpy(filename, ".");
		strcat(filename, uri);
		if (uri[strlen(uri) - 1] == '/')
			strcat(filename, "home.html");
		return 1;
	} else { /* Dynamic content */
		ptr = index(uri, '?');
		if (ptr) {
			strcpy(cgiargs, ptr + 1);
			*ptr = '\0';
		} else
			strcpy(cgiargs, "");
		strcpy(filename, ".");
		strcat(filename, uri);
		return 0;
	}
}

void get_filetype(char *filename, char *filetype) {
	if (strstr(filename, ".html"))
		strcpy(filetype, "text/html");
	else if (strstr(filename, ".gif"))
		strcpy(filetype, "image/gif");
	else if (strstr(filename, ".jpg"))
		strcpy(filetype, "image/jpeg");
	else
		strcpy(filetype, "text/plain");
}

/*void serve_dynamic(int fd, char *filename, char *cgiargs) {
 char buf[MAXLINE], *emptylist[] = { NULL };

 Return first part of HTTP response
 sprintf(buf, "HTTP/1.0 200 OK\r\n");
 Rio_writen(fd, buf, strlen(buf));
 sprintf(buf, "Server: Tiny Web Server\r\n");
 Rio_writen(fd, buf, strlen(buf));
 if (fork() == 0) {  child
 Real server would set all CGI vars here
 setenv("QUERY_STRING", cgiargs, 1);
 Dup2(fd, STDOUT_FILENO);
 Redirect stdout to client
 execve(filename, emptylist, environ);  Run CGI program
 }
 Wait(NULL);  Parent waits for and reaps child
 }*/

